home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / info-service / www / src / WWW / Daemon / Implementation / VMSHelpGate.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-02-26  |  14.0 KB  |  439 lines

  1. /*        Handle a VMS Help request from a WWW client    VMSHelpGate.c
  2. **        ===========================================
  3. **
  4. ** Authors
  5. **    JFG    Jean-Francois Groff, CERN, Geneva    jfg@info.cern.ch
  6. **
  7. ** History:
  8. ** 1.1  26 Feb 92 (JFG) Enabled strange topics
  9. ** 1.0     7 Oct 91 (JFG) First release
  10. ** 0.4     2 Oct 91 (JFG) Handle help summary. Definitive address format :
  11. **            //node[:port]/HELP[/@library][/topic[/subtopic...]]
  12. ** 0.3    27 Sep 91 (JFG)    Created from h2h.c
  13. */
  14.  
  15. /* (c) CERN WorldWideWeb project 1990-1992. See Copyright.html for details */
  16.  
  17.  
  18. /** Headers taken from HTRetrieve.c **/
  19.  
  20. #define BUFFER_SIZE 4096    /* Arbitrary size for efficiency */
  21.  
  22. #include "HTUtils.h"
  23. #include "tcp.h"
  24.  
  25. extern int WWW_TraceFlag;    /* Control diagnostic output */
  26. extern FILE * LogFile;        /* Log file output */
  27. extern char HTClientHost[16];    /* Client name to be output */
  28. extern int HTWriteASCII(int soc, char * s);    /* In HTDaemon.c */
  29.  
  30.  
  31. /** Headers taken from h2h.c **/
  32.  
  33. /* Maximum line size for buffers */
  34. #define LSIZE 256
  35.  
  36. /* Maximum command line size for VMS */
  37. #define CSIZE 256
  38.  
  39. /* Maximum VMS file name size (on 5.3, it's really 41, yuk! hope for more) */
  40. #define FSIZE 80
  41.  
  42. /* Maximum anchor size */
  43. #define ASIZE 80
  44.  
  45. /* Diagnostics processing utilities */
  46. static const char *module_name = "VMSHelpGate: ";
  47. char diag[LSIZE];  /* Diagnostic string */
  48. /* Horrible kludge, but saves a lot of pain */
  49. #define DIAGNOSE(sprintf_arg_list, error_code) { \
  50.   sprintf sprintf_arg_list; \
  51.   if (TRACE) fprintf (stderr, "%s%s", module_name, diag); \
  52.   HTWriteASCII (soc, diag); \
  53.   return error_code; \
  54.   }
  55.  
  56. /*******************************************************************************
  57.  
  58. anchor_strcpy : Converts a string to a name suitable for use as an anchor and
  59.                 for comparisons, i.e. trim it to its first word (consisting of
  60.         alphanumeric characters plus '_', '-', '$' and '/'),
  61.         make it uppercase and convert each '/', '-' and '$' to '_'.
  62.         If the string does not begin with a "good" character, escape it.
  63.  
  64. Inputs :
  65.     char *dest : destination string
  66.     char *src : source string
  67.  
  68. Returns the end position of the destination string.
  69.  
  70. ------------------------------------------------------------------------------*/
  71.  
  72. static char *anchor_strcpy
  73. #ifdef __STDC__
  74.   (char *dest, char *src)
  75. #else
  76.   (dest, src)
  77.     char *dest;
  78.     char *src;
  79. #endif
  80. {
  81.   char *end = dest;
  82.   /* Trim to first word */
  83.   for ( ; *end =
  84.             isalnum (*src) ? toupper(*src)
  85.           : *src == '/' || *src == '_' || *src == '-' || *src == '$' ? '_'
  86.           : '\0' ;
  87.        ++end, ++src);
  88.   if (end == dest) {  /* src doesn't begin with a word or /_-$ : escape it */
  89.     for ( ; *src ; ++src) {
  90.     *end++ =
  91.       isalnum (*src) ? toupper (*src)
  92.     : 'A' + (*src % 26);  /* Hash a letter from the bad character */
  93.     }
  94.     *end = 0;  /* Terminate the string */
  95.   }
  96.   return end;
  97. }
  98.  
  99. /*******************************************************************************
  100.  
  101. hlp_to_html : Reads in .HLP help file and outputs desired info in HTML format.
  102.  
  103. Inputs :
  104.     FILE *hlp : pointer to the .HLP file to be scanned
  105.     char *query : data to be found, as blank-separated keywords
  106.                   NOTE : this string is altered (tokenized) by the function
  107.     int soc : HTML output socket
  108.  
  109. Status returned :
  110.      0 : Success
  111. Unused    -1 : Help file not found
  112.     -2 : Query not found
  113. Unused    -3 : Empty query
  114.     -4 : Structure error in help file (may create some text before that)
  115. Unused    -5 : Write error to HTML file (may create some text before that)
  116. Unused    -6 : Can't open HTML output file
  117.  
  118. ------------------------------------------------------------------------------*/
  119.  
  120. static int hlp_to_html
  121. #ifdef __STDC__
  122.   (const FILE *hlp, const char *query, const int soc)
  123. #else
  124.   (hlp, query, soc)
  125.     FILE *hlp;
  126.     char *query;
  127.     int soc;
  128. #endif
  129. {
  130.   char *sep = " ";  /* Authorized keyword separators in the query */
  131.   char line[LSIZE];  /* Input buffer */
  132.   char out[LSIZE];  /* Output buffer */
  133.   /* Possible states of the scanning algorithm */
  134.   enum { PARSE, TEXT, MENU, DONE } state;
  135.   int depth;  /* Current depth in the help tree */
  136.   char *key;  /* Currently searched keyword from the query */
  137.   int prefix;  /* Prefix number on a line of the help file, indicating depth */
  138.   char *title;  /* Pointer to the rest of a title line after depth prefix */
  139.   char anchor[ASIZE];  /* Anchor name built from menu title and item */
  140.   char *menu_item;  /* Pointer to menu item within anchor name */
  141.   char *s, *t;  /* Temporary string pointers */
  142.  
  143.   /* Initial things to send out */
  144.   /* HTWriteASCII (soc, "<ISINDEX>\n");  /* DON'T Enable keyword searches */
  145.  
  146.   state = PARSE;  /* Searching for the query's keywords */
  147.   depth = 1;
  148.   key = strtok (query, sep);  /* Read first keyword from the query */
  149.   anchor_strcpy (key, key); /* Normalise key to help matches */
  150.  
  151.   while (fgets (line, LSIZE, hlp)) {
  152.     if (isdigit (*line)) {  /* This is a title line */
  153.       prefix = strtol (line, &title, 10);
  154.       while (isspace (*title))  /* Find the real beginning of the title */
  155.     title++;
  156.       s = title;
  157.       while (*(++s) != '\n');  /* Find the real end of the title */
  158.       while (isspace (*(--s)));
  159.       *(++s) = '\0';  /* Trim title to a clean string */
  160.  
  161.       switch (state) {
  162.       case PARSE:  /* Check title against query */
  163.     menu_item = anchor_strcpy (anchor, title);
  164.     /* Is this a match ? */
  165.     if (prefix == depth && !strncmp(anchor, key, strlen(key))) {
  166.       if (depth == 1) {  /* Root match */
  167.         /* Start the HTML title with the first word of 'title' */
  168.         sprintf (out, "<TITLE>Help %.*s", menu_item - anchor, title);
  169.         HTWriteASCII (soc, out);
  170.       } else {  /* Deep match : continue HTML title in the same way */
  171.         sprintf (out, " %.*s", menu_item - anchor, title);
  172.         HTWriteASCII (soc, out);
  173.       }
  174.       if (key = strtok (NULL, sep)) {  /* More levels to descend */
  175.         depth++;
  176.         anchor_strcpy (key, key); /* Normalise key to help matches */
  177.       }
  178.       else {  /* Query fully parsed */
  179.         HTWriteASCII (soc, "</TITLE>\n");  /* End the HTML title */
  180.         sprintf (out, "<H1>%s</H1>\n", title);  /* Print full heading */
  181.         HTWriteASCII (soc, out);
  182.         *menu_item++ = '/';  /* Append field separator to anchor */
  183.         *menu_item = '\0';  /* Ready for anchor completion */
  184.         state = TEXT;
  185.         HTWriteASCII (soc, "<XMP>\n");  /* Will copy help text verbatim */
  186.       }
  187.     }
  188.     break;
  189.  
  190.       case TEXT:
  191.     HTWriteASCII (soc, "</XMP>\n");  /* Help text is finished, folks ! */
  192.     if (prefix <= depth) {
  193.       /* We reached the next item at this depth or it was the last one */
  194.       state = DONE;
  195.       break;
  196.     } else if (prefix == depth + 1) {  /* This item has a menu */
  197.       state = MENU;
  198.       depth++;
  199.       HTWriteASCII (soc, "<H3>Additional information available:</H3>\n<DIR>\n");
  200.     } else {  /* The help file skipped the next depth */
  201.       fclose (hlp);
  202.       DIAGNOSE ((diag, "Help file corrupted.\n"), -4);
  203.     }
  204.     /* No break here : flow through case MENU if state transition */
  205.     /* (Yeah, you may find this ugly...) */
  206.  
  207.       case MENU:
  208.     if (prefix < depth) {  /* Seen all menu items */
  209.       HTWriteASCII (soc, "</DIR>\n");  /* That's all, folks ! */
  210.       state = DONE;
  211.     } else if (prefix == depth) {  /* Next menu item */
  212.       anchor_strcpy (menu_item, title);  /* Append item to anchor prefix */
  213.       sprintf (out, "<LI><A HREF=%s>%s</A>\n", anchor, title);
  214.       HTWriteASCII (soc, out);
  215.     } /* if prefix > depth, there's a submenu : ignore it. */
  216.     break;
  217.     
  218.       case DONE:  /* Shouldn't happen here */
  219.     fprintf (stderr, "Internal error : invalid state DONE.\n");
  220.     break;
  221.  
  222.       }  /* End of switch(state) */
  223.     }  /* End of title line processing */
  224.  
  225.     else { /* This is a text line */
  226.       if (state == TEXT && *line != '!')
  227.     /* Copy line verbatim to HTML file, except if it's a comment ('!') */
  228.     HTWriteASCII (soc, line);
  229.     }  /* End of text line processing */
  230.  
  231.     if (state == DONE) {  /* Have we finished yet ? */
  232.       fclose (hlp);
  233.       /* FIXME Insert here HTML file termination */
  234.       return 0;  /* success */
  235.     }
  236.  
  237.   }  /* EOF reached on help file */
  238.   fclose (hlp);
  239.   if (state != PARSE) {  /* EOF reached while outputting HTML */
  240.     if (state == MENU)
  241.       HTWriteASCII (soc, "</UL>\n");  /* End the menu cleanly */
  242.     return 0;  /* success */
  243.   } else {  /* key not found */
  244.     if (depth > 1)  /* Partial match : end the title cleanly */
  245.       HTWriteASCII (soc, "</TITLE>\n");
  246.     DIAGNOSE ((diag, "\"%s\" not found in help file.\n", key), -2);
  247.   }
  248. }
  249.  
  250.  
  251. /*******************************************************************************
  252.  
  253. lis_to_html : Reads in .LIS library directory and outputs menu in HTML format.
  254.  
  255. Inputs :
  256.     FILE *dir : pointer to the .LIS file to be scanned
  257.     char *lib_name : name of the help library (for HTML title)
  258.     int soc : HTML output socket
  259.  
  260. Status returned :
  261.      0 : Success
  262.     -2 : Structure error in .LIS file
  263.  
  264. ------------------------------------------------------------------------------*/
  265.  
  266. static int lis_to_html
  267. #ifdef __STDC__
  268.   (const FILE *dir, const char *lib_name, const int soc)
  269. #else
  270.   (dir, lib_name, soc)
  271.     FILE *dir;
  272.     char *lib_name;
  273.     int soc;
  274. #endif
  275. {
  276.   char line[LSIZE];  /* Input buffer */
  277.   char out[LSIZE];  /* Output buffer */
  278.   char *entry;  /* Pointer to current directory entry */
  279.   char *at_prefix;
  280.   
  281.   if (lib_name)  /* Help library specified */
  282.     at_prefix = "@";
  283.   else {  /* Plain HELP request */
  284.     at_prefix = "";
  285.     lib_name = "HELP";  /* Root level help */
  286.   }
  287.     
  288.   /* Initial things to send out */
  289. /*  HTWriteASCII (soc, "<ISINDEX>\n<TITLE>Help Library "); */
  290.   HTWriteASCII (soc, "<TITLE>Help Library ");
  291.   HTWriteASCII (soc, lib_name);
  292.   HTWriteASCII (soc, " Contents</TITLE>\n<H1>");
  293.   HTWriteASCII (soc, lib_name);
  294.   HTWriteASCII (soc, "</H1>\n<DIR>\n");
  295.  
  296.   do {  /* Find the first empty line */
  297.     if (! fgets (line, LSIZE, dir))
  298.       DIAGNOSE ((diag, "Structure error in help library directory.\n"), -2);
  299.   } while (*line != '\n');
  300.  
  301.   /* Now scan the entries */
  302.   while (fgets (line, LSIZE, dir) && (entry = strtok (line, " \n"))) {
  303.     sprintf (out, "<LI><A HREF=%s%s/%s>%s</A>\n",
  304.          at_prefix, lib_name, entry, entry);
  305.     HTWriteASCII (soc, out);
  306.   }  /* End of entry processing */
  307.  
  308.   fclose (dir);
  309.   HTWriteASCII (soc, "</DIR>\n");  /* End the menu cleanly */
  310.   return 0;  /* success */
  311. }
  312.  
  313.  
  314. /*******************************************************************************
  315.  
  316. open_hlp : Opens desired .HLP module from specified library. Extracts it from
  317.            .HLB help library if necessary. If no module is specified, opens
  318.        (and perhaps creates) .LIS library directory file.
  319.  
  320. Inputs :
  321.     char *help_lib : library name
  322.     char *module : name of the module to extract
  323.  
  324. Returns FILE * pointing to relevant .HLP or .LIS if success, NULL on error.
  325.  
  326. ------------------------------------------------------------------------------*/
  327.  
  328. static FILE *open_hlp
  329. #ifdef __STDC__
  330.   (const char *help_lib, const char *module, const int soc)
  331. #else
  332.   (help_lib, module, soc)
  333.     char *help_lib;
  334.     char *module;
  335.     int soc;
  336. #endif
  337. {
  338.   char hlpfile[FSIZE];
  339.   char *help_prefix;
  340.   char *s, *t;
  341.   FILE *hlp;
  342.   char command[CSIZE];
  343.  
  344.   /* First, create the relevant (unique) file name */
  345.   if (! help_lib)  /* Plain HELP request */
  346.     help_lib = "HELPLIB";  /* Open default help library */
  347.   s = strchr (help_lib, ':');
  348.   help_prefix = s ? '\0' : "SYS$HELP:";
  349.   t = strchr (help_lib, ']');
  350.   s = (t && t > s) ? t + 1 : (s ? s + 1 : help_lib);  /* Find beginning */
  351.   if (! (t = strrchr (help_lib, '.')))  /* Find end */
  352.     t = help_lib + strlen (help_lib);
  353.   if (module) {
  354.     sprintf (hlpfile, "WWW$TEMP_DIR:%.*s_", t - s, s);
  355.     anchor_strcpy (hlpfile + strlen (hlpfile), module);  /* Append valid module name */
  356.     strcat (hlpfile, ".HLP");
  357.   }
  358.   else
  359.     sprintf (hlpfile, "WWW$TEMP_DIR:%.*s.LIS", t - s, s);
  360.   if (hlp = fopen (hlpfile, "r"))  /* Does it already exist ? */
  361.     return hlp;  /* Yes : return it, else create it */
  362.   if (module)
  363.     sprintf (command, "LIBRARY /HELP %s%s /EXTRACT=\"%s\" /OUTPUT=%s",
  364.          help_prefix, help_lib, module, hlpfile);
  365.   else
  366.     sprintf (command, "LIBRARY /HELP %s%s /LIST=%s",
  367.          help_prefix, help_lib, hlpfile);
  368.   if (! (system (command)) & 1) {  /* VMS error status ? */
  369.     if (! module)
  370.       return NULL;  /* Failure */
  371.     /* Try again in default help libraries (FIXME scan a list here) */
  372.     sprintf (command, "LIBRARY /HELP SYS$HELP:HELPLIB /EXTRACT=\"%s\" /OUTPUT=%s",
  373.        module, hlpfile);
  374.     if (! (system (command)) & 1)  /* VMS error status ? */
  375.       return NULL;  /* Failure */
  376.   }
  377.   return fopen (hlpfile, "r");
  378. }
  379.  
  380.  
  381. /*******************************************************************************
  382.  
  383. HTRetrieve : Retrieves information from VMS help library.
  384.  
  385. Inputs :
  386.     char *arg : HT address
  387.     char *keywords : plus-separated keyword list, if any
  388.     int soc : output socket
  389.  
  390. WARNING : This function relies on the fact that the keywords string
  391.           immediately follows the arg string. It also modifies those strings.
  392.  
  393. ------------------------------------------------------------------------------*/
  394.  
  395. int HTRetrieve
  396. #ifdef __STDC__
  397.   (const char *arg, const char *keywords, const int soc)
  398. #else
  399.   (arg, keywords, soc)
  400.     char *arg;
  401.     char *keywords;
  402.     int soc;
  403. #endif
  404. {
  405.   FILE *hlp;
  406.   char *help_file;
  407.   char *query;
  408.   char *s;
  409.  
  410.   if (keywords)
  411.     *(keywords - 1) = '/';  /* Un-split arg from keywords */
  412.   /* address must be of the form /HELP[/@library][/topic[/subtopic...]] */
  413.   s = strtok (arg + 1, "/");
  414.   if (strcasecomp (s, "HELP")) {
  415.     DIAGNOSE ((diag, "Address should begin with /HELP : /%s\n", s), -1)
  416.   }
  417.   help_file = strtok (NULL, "/");
  418.   if (help_file && *help_file == '@'
  419.       && help_file[1] ) {  /* Explicit help library specified */
  420.     help_file++;  /* Skip @ */
  421.     query = strtok (NULL, "/+");
  422.   } else {  /* This was not a help library, but the beginning of the query */
  423.     query = help_file;
  424.     help_file = NULL;
  425.   }
  426.   /* The first word from the query is the module name (perhaps empty) */
  427.   if (! (hlp = open_hlp (help_file, query, soc))) {
  428.     DIAGNOSE ((diag, "Help library or module not found : %s/%s\n",
  429.            help_file, query), -1)
  430.   }
  431.   if (query) {  /* Parse rest of address and process help file */
  432.     while (s = strtok (NULL, "/+"))
  433.       *(s - 1) = ' ';
  434.     return hlp_to_html (hlp, query, soc);
  435.   }
  436.   else  /* Process library directory */
  437.     return lis_to_html (hlp, help_file, soc);
  438. }
  439.